package com.car.utils;

import com.alibaba.druid.support.logging.Log4jImpl;
import com.car.config.DataSourceConfig;

import java.lang.reflect.Method;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

/**
 * 数据库操作工具类
 */
public class JDBCUtil {
    /**
     * 得到预编译对象
     *
     * @param sql    sql
     * @param params 参数
     * @return 编译后的预编译对象
     * @throws Exception 异常
     */
    public static PreparedStatement getPreparedStatement(String sql, Object... params)
            throws Exception {
        // 获取连接对象
        Connection connection = DataSourceConfig.getConnection();
        // 获取预编译对象
        PreparedStatement ps = connection.prepareStatement(sql);
        // 设置参数
        // 参数的元数据
        ParameterMetaData parameterMetaData = ps.getParameterMetaData();
        // 获取多少个问号
        int parameterCount = parameterMetaData.getParameterCount();
        if (params.length != parameterCount) {
            throw new RuntimeException("参数个数不匹配！");
        }

        for (int i = 1; i <= parameterCount; i++) {
            ps.setObject(i, params[i - 1]);
        }

        System.out.println(ps);
        return ps;
    }

    /**
     * DDL DML语句操作
     *
     * @param sql    sql
     * @param params 参数
     * @return 影响记录数
     * @throws Exception 异常
     */
    public static int update(String sql, Object... params) throws Exception {
        PreparedStatement ps = getPreparedStatement(sql, params);
        // 执行sql
        int i = ps.executeUpdate();
        // 关闭资源
        close(ps, ps.getConnection());
        return i;
    }

    /**
     * 我们写一个sql 它能够 帮我们直接返回我们要的实体类
     *
     * @param sql    sql
     * @param c      类型
     * @param params 参数
     * @param <T>    类型
     * @return 实体类
     * @throws Exception 异常
     */
    public static <T> T query(String sql, Class<T> c, Object... params) throws Exception {
        PreparedStatement ps = getPreparedStatement(sql, params);
        ResultSet rs = ps.executeQuery();
        // 获取ResultSet元数据 "id"	"name"	"age"	"gender"	"work_id"	"salary"
        ResultSetMetaData metaData = rs.getMetaData();
        // 元数据的个数 == 》 数据库的字段个数
        int columnCount = metaData.getColumnCount();
        // 实例化实体类
        T t = null;
        while (rs.next()) {
            t = c.getConstructor().newInstance();
            for (int i = 1; i <= columnCount; i++) {
                // 将数据的字段名 转换成 实体类的字段名
                String filedName = convertFiledName(metaData.getColumnName(i));
                // 通过字段获取方法
                Method method = getMethodByFiledName(c, filedName);
                // 执行方法
                method.invoke(t, rs.getObject(i));
            }
        }
        //关闭资源
        close(ps, ps.getConnection(), rs);
        return t;
    }

    public static <T> List<T> queryList(String sql, Class<T> c, Object... params) throws Exception {
        PreparedStatement ps = getPreparedStatement(sql, params);
        ResultSet rs = ps.executeQuery();
        // 获取ResultSet元数据
        ResultSetMetaData metaData = rs.getMetaData();
        // 元数据的个数
        int columnCount = metaData.getColumnCount();
        // 创建集合
        List<T> list = new ArrayList<>();
        while (rs.next()) {
            // 实例化实体类
            T t = c.getConstructor().newInstance();
            for (int i = 1; i <= columnCount; i++) {
                String filedName = convertFiledName(metaData.getColumnName(i));
                // 通过字段获取set方法
                Method method = getMethodByFiledName(c, filedName);
                // 执行方法
                method.invoke(t, rs.getObject(i));
            }
            list.add(t);
        }
        //关闭资源
        close(ps, ps.getConnection(), rs);
        return list;
    }

    /**
     * 通过字段名 或者Set反方法
     *
     * @param c         类型
     * @param filedName 字段名
     * @param <T>       泛型
     * @return Set方法
     */
    private static <T> Method getMethodByFiledName(Class<T> c, String filedName) {
        Method[] methods = c.getMethods();
        // 将首字母大写 如：id => Id
        String name = (char) (filedName.charAt(0) - 32) + filedName.substring(1);
        for (Method method : methods) {
            if (method.getName().equals("set" + name)) {
                return method;
            }
        }
        return null;
    }


    /**
     * 将数据库的字段 转换成实体类的字段
     *
     * @param name 数据库的字段
     * @return 实体类的字段
     */
    private static String convertFiledName(String name) {
        if (!name.contains("_")) {
            return name;
        }
        StringBuilder sb = new StringBuilder();
        // 通过下划线分割
        String[] split = name.split("_");
        sb.append(split[0]);
        for (int i = 1; i < split.length; i++) {
            // 将首字母大写
            sb.append((char) (split[i].charAt(0) - 32));
            // 剩下其余字符串添加
            sb.append(split[i].substring(1));
        }
        return sb.toString();
    }

    /**
     * 关闭资源
     *
     * @param es  数组-资源对象
     * @param <E> 资源对象
     * @throws Exception 异常
     */
    @SafeVarargs
    public static <E extends AutoCloseable> void close(E... es) throws Exception {
        for (E e : es) {
            if (e != null) {
                e.close();
            }
        }
    }
}
